home *** CD-ROM | disk | FTP | other *** search
- /*
- * apcd.c - Daemon for the APC Smart UPS
- *
- * Copyright (c) 1995 Pavel Korensky
- * All rights reserved.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL PAVEL KORENSKY BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
- * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF PAVEL
- * KORENSKY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * PAVEL KORENSKY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND PAVEL KORENSKY HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- */
-
- /*
- * Version:
- *
- * $Id: apcd.c,v 1.7 1995/11/07 12:40:03 root Exp root $
- *
- *
- * History:
- *
- * $Log: apcd.c,v $
- * Revision 1.7 1995/11/07 12:40:03 root
- * Version 0.5 Beta, uploaded to the sunsite
- *
- * Revision 1.6 1995/11/01 15:25:28 root
- * Several adaptations for clien/server - NOT FUNCTIONAL
- *
- * Revision 1.5 1995/05/23 07:25:08 root
- * First public ALPHA version
- *
- * Revision 1.4 1995/05/23 01:07:40 root
- * Parameters are on the command line, instead of config.h file
- *
- * Revision 1.3 1995/05/23 00:25:43 root
- * System shutdown with UPS switch off was added
- *
- * Revision 1.2 1995/05/21 21:10:56 root
- * Some small fixes
- *
- * Revision 1.1 1995/05/21 20:15:13 root
- * Initial revision
- *
- *
- *
- *
- *
- */
-
-
- #include "apcd.h"
- #include "version.h"
-
- static char *version="$Id: apcd.c,v 1.7 1995/11/07 12:40:03 root Exp root $";
-
- UPSINFO myUPS;
- FILE *valfile;
- FILE *UPSlogfile;
- int killme, battlow;
- int slave = 0;
- int port;
- int socketfd,newsocketfd;
- char *use_port;
- char *master_name;
- char *logfilename;
- char *slaves[MAX_SLAVES];
- int num_slaves = 0;
- int power_timer = 10;
- int log_timer = 30;
- int log_counter = 0;
- int alarmup,alarmdown,pending,alarmcount,wasmsg;
- int gottimeout = 0;
- int mastertimeout = 0;
- int gotpowerok = 0;
- int masterbatlow = 0;
- struct termios oldtio, newtio;
-
-
- void main(int argc, char *argv[])
- {
- char msg[100];
- int i;
- time_t cas;
-
- time(&cas);
- strftime(msg,100,"%b %d %X",localtime(&cas));
- printf("%s apcd:\n",msg);
- use_port=calloc(100,sizeof(char));
- master_name=calloc(100,sizeof(char));
- logfilename=calloc(100,sizeof(char));
- for(i=0;i<=MAX_SLAVES;i++) slaves[i]=calloc(100,sizeof(char));
-
- if(parse_config()==0) {
- printf("\nConfiguration file is bad or missing\n");
- exit(0);
- }
- if(slave==2) {
- printf("APC SmartUPS daemon started on port %s with timeout %d mins.\n",use_port,power_timer);
- printf("APC SmartUPS daemon logging interval %d seconds.\n",log_timer);
- for(i=0;i<num_slaves;i++) printf("Slave: %s\n",slaves[i]);
- }
- else printf("APC SmartUPS daemon started in slave mode. Master is %s\n",master_name);
- killme = 0;
- /* Initialize system log */
- openlog("apcd", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_LOCAL2);
- /* If we are master, we are responsible for UPS statistic log */
- if(slave == 2) {
- UPSlogfile=fopen(logfilename,"a");
- }
-
- /* Become daemon */
- start_daemon();
- signal_setup();
- syslog(LOG_INFO,"Starting apcd version %s",VERSION);
- if(slave==2) {
- syslog(LOG_INFO,"Master mode port %s timeout %d",use_port,power_timer);
- syslog(LOG_INFO,"UPS statistics in %s",logfilename);
- for(i=0;i<num_slaves;i++) syslog(LOG_INFO,"Slave: %s",slaves[i]);
- }
- if(slave==1) syslog(LOG_INFO,"Slave mode. Master is %s",master_name);
- if(slave==2) setup_tty();
- battlow=0;
- alarmup=0;
- alarmdown=0;
- pending=0;
- wasmsg=0;
- /* Open socket for network communication */
- if(slave == 1) {
- if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- syslog(LOG_ERR,"Can't open stream socket");
- }
- }
- if(slave == 2) {
- prepare_master();
- }
- if(slave == 1) prepare_slave(); /* If slave, do some network stuff */
- if(slave == 1) {
- while(!killme) {
- if(get_master_message(newsocketfd) == 0) {
- if(gottimeout) {
- gottimeout = 0;
- syslog(LOG_INFO,"Power failure, %d minutes to shutdown",mastertimeout);
- if(mastertimeout == 0) go_down();
- else {
- sprintf(msg,"\n\nAPC Daemon: Power failure, system will go down in %d minutes.\n",mastertimeout);
- mesall(msg);
- wasmsg=1;
- }
- }
- if(gotpowerok) {
- gotpowerok = 0;
- syslog(LOG_INFO,"Power restored, shutdown cancelled");
- if(wasmsg) {
- sprintf(msg,"\n\nAPC Daemon: Power restored, shutdown cancelled.\n");
- mesall(msg);
- wasmsg=0;
- }
- }
- if(masterbatlow) go_down_batt();
-
- }
- }
- }
- else {
- while(!killme) {
- fillUPS(port,&myUPS);
- sleep(1);
- if (pending) {
- alarmcount--;
- if (alarmcount == 0) {
- send_to_slaves(0);
- go_down();
- pending=0;
- }
- if(((alarmcount % 60) == 0) && (alarmcount != 0)) {
- send_to_slaves(alarmcount);
- sprintf(msg,"\n\nAPC Daemon: Power failure, system will go down in %d minutes.\n",alarmcount/60);
- mesall(msg);
- wasmsg=1;
- }
- }
- if (alarmup) {
- if(!pending) {
- alarmcount=power_timer*60;
- pending=1;
- }
- alarmup=0;
- }
- if (alarmdown) {
- if(wasmsg) {
- send_to_slaves(-1);
- sprintf(msg,"\n\nAPC Daemon: Power restored, shutdown cancelled\n");
- mesall(msg);
- wasmsg=0;
- }
- alarmcount=0;
- pending=0;
- alarmdown=0;
- }
- if (battlow) go_down_batt();
-
- }
- }
- if(killme == 1) {
- syslog(LOG_INFO,"Ending apcd version %s",VERSION);
- if(slave==2) {
- tcsetattr(port,TCSANOW,&oldtio);
- close(port);
- }
- if (slave==1) close(socketfd);
- if (slave==2) {
- for(i=0;i<num_slaves;i++) close(slavesocket[i]);
- }
- closelog();
- if (slave == 1) fclose(UPSlogfile);
- }
- if(killme == 2) {
- if(slave==2) send_to_slaves(-2);
- mesall("APC Daemon: SYSTEM IS GOING DOWN NOW !!!");
- if (slave==1) close(socketfd);
- if (slave==2) {
- for(i=0;i<num_slaves;i++) close(slavesocket[i]);
- }
- do_shutdown();
- }
- };
-
-
- /* Setup of the communication port. Hope it will work
- */
-
- void setup_tty()
- {
- port=open(use_port,O_RDWR | O_NOCTTY);
- if (port < 0) {
- syslog(LOG_ERR,"Unable to open port %s",use_port);
- exit(-1);
- }
- tcgetattr(port,&oldtio); /* Save old settings */
- newtio.c_cflag = DEFAULT_SPEED | CS8 | CLOCAL | CREAD;
- newtio.c_iflag = IGNPAR; /* Ignore errors, raw input */
- newtio.c_oflag = 0; /* Raw output */
- newtio.c_lflag = 0; /* No local echo */
- newtio.c_cc[VMIN] = 1;
- newtio.c_cc[VTIME] = 0;
- tcflush(port,TCIFLUSH);
- tcsetattr(port,TCSANOW,&newtio);
- }
-
-
- /* Become a daemon, release stdin, stdout etc.
- */
-
- void start_daemon()
- {
- int pid;
-
- close(0);
- close(1);
- close(2);
- if ((pid=fork()) < 0) {
- syslog(LOG_ERR,"Unable to fork");
- exit(1);
- }
- if (pid != 0) exit(0);
- };
-
-
- /* Setup various signal handlers. Code here is adapted from diald program
- * which is (c) Eric Schenk.
- */
-
- void signal_setup()
- {
- sigset_t sigmask;
- struct sigaction sa;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGINT); /* Termination requested */
- sigaddset(&sigmask, SIGTERM); /* Termination requested */
- if(slave==2) sigaddset(&sigmask, SIGUSR1); /* Dump UPS stats */
-
- #define SIGNAL(s,handler) { \
- sa.sa_handler = handler; \
- if (sigaction(s, &sa, NULL) < 0) { \
- syslog(LOG_ERR, "sigaction(%d) failed ", s); \
- exit(1); \
- } \
- }
-
- sa.sa_mask = sigmask;
- sa.sa_flags = 0;
- SIGNAL(SIGINT,sig_intr);
- SIGNAL(SIGTERM,sig_term);
- if(slave==2) SIGNAL(SIGUSR1,dump_status);
-
- }
-
- void sig_intr(int sig)
- {
- syslog(LOG_INFO,"SIGINTR Termination requested");
- killme = 1;
- }
-
- void sig_term(int sig)
- {
- syslog(LOG_INFO,"SIGTERM Termination requested");
- killme=1;
- }
-
- void dump_status(int sig)
- {
- valfile=fopen("/tmp/upsstat","w");
- fprintf(valfile,"ULINE:%.1f\n",myUPS.LineVoltage);
- fprintf(valfile,"MLINE:%.1f\n",myUPS.LineMax);
- fprintf(valfile,"NLINE:%.1f\n",myUPS.LineMin);
- fprintf(valfile,"FLINE:%.1f\n",myUPS.LineFreq);
- fprintf(valfile,"VOUTP:%.1f\n",myUPS.OutputVoltage);
- fprintf(valfile,"LOUTP:%.1f\n",myUPS.UPSLoad);
- fprintf(valfile,"BOUTP:%.1f\n",myUPS.BattVoltage);
- fprintf(valfile,"BCHAR:%.1f\n",myUPS.BatLoad);
- fprintf(valfile,"BFAIL:%d\n",battlow);
- fprintf(valfile,"UTEMP:%.1f\n",myUPS.UPSTemp);
- if(pending) {
- fprintf(valfile,"UBATT:1\n");
- fprintf(valfile,"UPOWR:0\n");
- }
- else {
- fprintf(valfile,"UBATT:0\n");
- fprintf(valfile,"UPOWR:1\n");
- };
- fprintf(valfile,"UTST1:NONE\n");
- fprintf(valfile,"UTST2:NONE\n");
- fprintf(valfile,"UTST3:NONE\n");
- fprintf(valfile,"UTST4:NONE\n");
- fprintf(valfile,"UTST5:NONE\n");
- fprintf(valfile,"UTST6:NONE\n");
- fprintf(valfile,"UTST7:NONE\n");
- fprintf(valfile,"UTST8:NONE\n");
- fprintf(valfile,"UTST9:NONE\n");
- fprintf(valfile,"UTST0:NONE\n");
- fprintf(valfile,"UUSR1:NONE\n");
- fprintf(valfile,"UUSR2:NONE\n");
- fprintf(valfile,"UUSR3:NONE\n");
- fprintf(valfile,"UUSR4:NONE\n");
- fprintf(valfile,"UUSR5:NONE\n");
- fprintf(valfile,"UUSR6:NONE\n");
- fprintf(valfile,"UUSR7:NONE\n");
- fprintf(valfile,"UUSR8:NONE\n");
- fprintf(valfile,"UUSR9:NONE\n");
- fprintf(valfile,"UUSR0:NONE\n");
- fclose(valfile);
- }
-
-
- void go_down()
- {
- syslog(LOG_INFO,"System is going down - power failure");
- killme=2;
- }
-
- void go_down_batt()
- {
- syslog(LOG_INFO,"System is going down - battery low");
- killme=2;
- }
-
-
- int getline(int fd, char *s)
- {
- int i,ending;
- char c;
-
- i=0;
- ending=0;
-
- while (!ending) {
- read(fd,&c,1);
- switch(c) {
- case UPS_ON_BATT: syslog(LOG_INFO,"UPS is going on battery");
- alarmup=1;
- break;
- case UPS_ON_LINE: syslog(LOG_INFO,"UPS is going on-line");
- alarmdown=1;
- break;
- case BATT_LOW: battlow=1;
- break;
- case BATT_OK: battlow=0;
- break;
- case '\n': ending=1;
- break;
- default: s[i++]=c;
- break;
- }
- }
- s[i]='\0';
- return(0);
- }
-
- int fillUPS (int fd,UPSINFO *ups)
- {
- char answer[MAXLINE];
- char q;
-
- q='Y';
- write(fd,&q,1);
- getline(fd,answer);
-
- q=BATT_FULL;
- write(fd,&q,1);
- getline(fd,answer);
- ups->BatLoad=atof(answer);
-
- q=UPS_LINE_MIN;
- write(fd,&q,1);
- getline(fd,answer);
- ups->LineMin=atof(answer);
-
- q=UPS_LINE_MAX;
- write(fd,&q,1);
- getline(fd,answer);
- ups->LineMax=atof(answer);
-
- q=UPS_LOAD;
- write(fd,&q,1);
- getline(fd,answer);
- ups->UPSLoad=atof(answer);
-
- q=LINE_FREQ;
- write(fd,&q,1);
- getline(fd,answer);
- ups->LineFreq=atof(answer);
-
- q=LINE_VOLTAGE;
- write(fd,&q,1);
- getline(fd,answer);
- ups->LineVoltage=atof(answer);
-
- q=OUTPUT_VOLTAGE;
- write(fd,&q,1);
- getline(fd,answer);
- ups->OutputVoltage=atof(answer);
-
- q=UPS_TEMP;
- write(fd,&q,1);
- getline(fd,answer);
- ups->UPSTemp=atof(answer);
-
- q=BATT_VOLTAGE;
- write(fd,&q,1);
- getline(fd,answer);
- ups->BattVoltage=atof(answer);
-
- q=UPS_STATUS;
- write(fd,&q,1);
- getline(fd,answer);
- ups->Status=atoi(answer);
-
- log_counter++;
- if(log_counter >= log_timer) {
- log_counter=0;
- log_UPS_status();
- }
- return(0);
- }
-
-
- /* mesusr() and mesall() function are actually parts of shutdown source
- * I am using them for sending messages before shutdown
- */
-
- void mesusr(char *mess,struct utmp *ut)
- {
- int fd;
- char term[40] = {'/','d','e','v','/',0};
-
- (void)strncat(term,ut->ut_line,sizeof(ut->ut_line));
- if((fd=open(term, O_RDWR | O_NONBLOCK)) < 0)
- return;
- write(fd,mess,strlen(mess));
- close(fd);
- }
-
- void mesall(char *mess)
- {
- struct utmp *ut;
- utmpname(_PATH_UTMP);
- setutent();
- ut=getutent();
- while((ut = getutent())) {
- if(ut->ut_type == USER_PROCESS)
- mesusr(mess,ut);
- }
- endutent();
- }
-
-
- /*
- *
- * From here, there are parts of the source from shutdown.c which
- * is a part of linux-utils-2.1
- *
- */
-
- void write_wtmp(), unmount_disks(), unmount_disks_ourselves();
-
-
-
- void
- do_shutdown()
- {
- /* struct itimerval new,old; */
- char a;
-
- /* setpriority(PRIO_PROCESS, 0, PRIO_MIN); */
-
- chdir("/");
-
- signal(SIGPIPE, SIG_IGN);
- signal(SIGINT, SIG_IGN);
-
- /* do syslog message... */
- syslog(LOG_INFO, "System cleanup");
- closelog();
- if (slave == 2) fclose(UPSlogfile);
- sleep(1);
- kill(1, SIGTSTP); /* tell init not to spawn more getty's */
- write_wtmp();
- sync();
- signal(SIGTERM, SIG_IGN);
- setpgrp(); /* so the shell wont kill us in the fall */
- /* a gentle kill of all other processes except init */
- kill(-1, SIGTERM);
- sleep(2);
-
- /* now use brute force... */
- kill(-1, SIGKILL);
-
- /* turn off accounting */
- acct(NULL);
-
- sync();
- sleep(2);
- /* unmount disks... */
- unmount_disks();
- sync();
- sleep(1);
- a = 'S';
- if(slave==2) write(port,&a,1);
- sleep(9999); /* Wait for UPS switch off */
- reboot(0xfee1dead,672274793,0x1234567); /* Just for sure :-) */
- /* NOTREACHED */
- exit(0); /* to quiet gcc */
- }
-
-
- void
- write_wtmp()
- {
- /* write in wtmp that we are dying */
- int fd;
- struct utmp ut;
-
- memset((char *)&ut, 0, sizeof(ut));
- strcpy(ut.ut_line, "~");
- memcpy(ut.ut_name, "shutdown", sizeof(ut.ut_name));
-
- time(&ut.ut_time);
- ut.ut_type = BOOT_TIME;
-
- if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) > 0) {
- write(fd, (char *)&ut, sizeof(ut));
- close(fd);
- }
- }
-
- void
- unmount_disks()
- {
- /* better to use umount directly because it may be smarter than us */
-
- int pid;
- int result;
- int status;
-
- sync();
- if ((pid = fork()) < 0) {
- printf("Cannot fork for umount, trying manually.\n");
- unmount_disks_ourselves();
- return;
- }
- if (!pid) {
- execl(_PATH_UMOUNT, UMOUNT_ARGS, NULL);
- printf("Cannot exec %s, trying umount.\n", _PATH_UMOUNT);
- execlp("umount", UMOUNT_ARGS, NULL);
- printf("Cannot exec umount, trying manually.\n");
- unmount_disks_ourselves();
- exit(0);
- }
- while ((result = wait(&status)) != -1 && result != pid)
- ;
- if (result == -1 || status) {
- printf("Running umount failed, trying manually.\n");
- unmount_disks_ourselves();
- }
- }
-
- void
- unmount_disks_ourselves()
- {
- /* unmount all disks */
-
- FILE *mtab;
- struct mntent *mnt;
- char *mntlist[128];
- int i;
- int n;
- char *filesys;
-
- sync();
- if (!(mtab = setmntent(_PATH_MTAB, "r"))) {
- printf("Cannot open %s.\n", _PATH_MTAB);
- return;
- }
- n = 0;
- while (n < 100 && (mnt = getmntent(mtab))) {
- mntlist[n++] = strdup(mnt->mnt_fsname[0] == '/' ?
- mnt->mnt_fsname : mnt->mnt_dir);
- }
- endmntent(mtab);
-
- /* we are careful to do this in reverse order of the mtab file */
-
- for (i = n - 1; i >= 0; i--) {
- filesys = mntlist[i];
- #ifdef DEBUGGING
- printf("umount %s\n", filesys);
- #else
- if (umount(mntlist[i]) < 0)
- printf("Couldn't umount %s\n", filesys);
- #endif
- }
- }
-
- void log_UPS_status()
- {
- char msg[100];
- time_t nowtime;
-
- time(&nowtime);
- strftime(msg,100,"%b %d %X",localtime(&nowtime));
- fprintf(UPSlogfile,"%s APC: ",msg);
- fprintf(UPSlogfile,"%.1f %.1f %.1f ",myUPS.BatLoad,myUPS.LineMin,myUPS.LineMax);
- fprintf(UPSlogfile,"%.1f %.1f %.1f ",myUPS.UPSLoad,myUPS.LineFreq,myUPS.LineVoltage);
- fprintf(UPSlogfile,"%.1f %.1f %.1f\n",myUPS.OutputVoltage,myUPS.UPSTemp,myUPS.BattVoltage);
- fflush(UPSlogfile);
- }
-
-
-